import { Color } from '../../Source/Cesium.js';
import { IndexDatatype } from '../../Source/Cesium.js';
import { Buffer } from '../../Source/Cesium.js';
import { BufferUsage } from '../../Source/Cesium.js';
import { Context } from '../../Source/Cesium.js';
import { ContextLimits } from '../../Source/Cesium.js';
import createContext from '../createContext.js';

describe('Renderer/Context', function() {

    var context;

    beforeAll(function() {
        context = createContext();
    });

    afterAll(function() {
        context.destroyForSpecs();
    });

    it('has a unique ID', function() {
        var c = createContext();
        expect(c.id).toBeDefined();
        expect(c.id).not.toEqual(context.id);
        c.destroyForSpecs();
    });

    it('get canvas', function() {
        expect(context.canvas).not.toBeNull();
    });

    it('get stencilBits', function() {
        expect(context.stencilBits).toBeGreaterThanOrEqualTo(0);
    });

    it('get maximumCombinedTextureImageUnits', function() {
        expect(ContextLimits.maximumCombinedTextureImageUnits).toBeGreaterThanOrEqualTo(8);
    });

    it('get maximumCubeMapSize', function() {
        expect(ContextLimits.maximumCubeMapSize).toBeGreaterThanOrEqualTo(16);
    });

    it('get maximumFragmentUniformVectors', function() {
        expect(ContextLimits.maximumFragmentUniformVectors).toBeGreaterThanOrEqualTo(16);
    });

    it('get maximumTextureImageUnits', function() {
        expect(ContextLimits.maximumTextureImageUnits).toBeGreaterThanOrEqualTo(8);
    });

    it('get maximumRenderbufferSize', function() {
        expect(ContextLimits.maximumRenderbufferSize).toBeGreaterThanOrEqualTo(1);
    });

    it('get maximumTextureSize', function() {
        expect(ContextLimits.maximumTextureSize).toBeGreaterThanOrEqualTo(64);
    });

    it('get maximumVaryingVectors', function() {
        expect(ContextLimits.maximumVaryingVectors).toBeGreaterThanOrEqualTo(8);
    });

    it('get maximumVertexAttributes', function() {
        expect(ContextLimits.maximumVertexAttributes).toBeGreaterThanOrEqualTo(8);
    });

    it('get maximumVertexTextureImageUnits', function() {
        expect(ContextLimits.maximumVertexTextureImageUnits).toBeGreaterThanOrEqualTo(0);
    });

    it('get maximumVertexUniformVectors', function() {
        expect(ContextLimits.maximumVertexUniformVectors).toBeGreaterThanOrEqualTo(1);
    });

    it('get minimumAliasedLineWidth', function() {
        expect(ContextLimits.minimumAliasedLineWidth).toBeLessThanOrEqualTo(1);
    });

    it('get maximumAliasedLineWidth', function() {
        expect(ContextLimits.maximumAliasedLineWidth).toBeGreaterThanOrEqualTo(1);
    });

    it('get minimumAliasedPointSize', function() {
        expect(ContextLimits.minimumAliasedPointSize).toBeLessThanOrEqualTo(1);
    });

    it('get maximumAliasedPointSize', function() {
        expect(ContextLimits.maximumAliasedPointSize).toBeGreaterThanOrEqualTo(1);
    });

    it('get maximumViewportWidth', function() {
        expect(ContextLimits.maximumViewportWidth).toBeGreaterThan(0);
    });

    it('get maximumViewportHeight', function() {
        expect(ContextLimits.maximumViewportHeight).toBeGreaterThan(0);
    });

    it('gets antialias', function() {
        var c = createContext({
            webgl : {
                antialias : false
            }
        });
        expect(c.antialias).toEqual(false);
        c.destroyForSpecs();
    });

    it('gets the standard derivatives extension', function() {
        var fs = '';

        if (context.standardDerivatives && !context.webgl2) {
            fs += '#extension GL_OES_standard_derivatives : enable\n';
        }

        fs +=
            'void main()\n' +
            '{\n';

        if (context.standardDerivatives) {
            fs += '  gl_FragColor = vec4(dFdx(1.0), dFdy(1.0), 1.0, 1.0);\n';
        } else {
            fs += '  gl_FragColor = vec4(1.0);\n';
        }

         fs += '}';

        var expected = context.standardDerivatives ? [0, 0, 255, 255] : [255, 255, 255, 255];
        expect({
            context : context,
            fragmentShader : fs
        }).contextToRender(expected);
    });

    it('gets the element index uint extension', function() {
        if (context.elementIndexUint) {
            var buffer = Buffer.createIndexBuffer({
                context : context,
                sizeInBytes : 6,
                usage : BufferUsage.STREAM_DRAW,
                indexDatatype : IndexDatatype.UNSIGNED_INT
            });
            expect(buffer).toBeDefined();
            buffer.destroy();
        } else {
            expect(function() {
                Buffer.createIndexBuffer({
                    context : context,
                    sizeInBytes : 6,
                    usage : BufferUsage.STREAM_DRAW,
                    indexDatatype : IndexDatatype.UNSIGNED_INT
                });
            }).toThrowDeveloperError();
        }
    });

    it('gets the depth texture extension', function() {
        expect(context.depthTexture).toBeDefined();
    });

    it('gets the texture float extension', function() {
        expect(context.floatingPointTexture).toBeDefined();
    });

    it('gets whether the texture float has 6 places of precision', function() {
        expect(context.floatTextureSixPlaces).toBeDefined();
    });

    it('gets texture filter anisotropic extension', function() {
        expect(context.textureFilterAnisotropic).toBeDefined();
    });

    it('gets maximum texture filter anisotropy', function() {
        if (context.textureFilterAnisotropic) {
            expect(ContextLimits.maximumTextureFilterAnisotropy).toBeGreaterThanOrEqualTo(2);
        } else {
            expect(ContextLimits.maximumTextureFilterAnisotropy).toEqual(1);
        }
    });

    it('gets vertex array object extension', function() {
        expect(context.vertexArrayObject).toBeDefined();
    });

    it('get the fragment depth extension', function() {
        if (context.fragmentDepth && !context.webgl2) {
            return;
        }

        var fs =
            'void main()\n' +
            '{\n' +
            '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
            '}';

        expect({
            context : context,
            fragmentShader : fs,
            depth : 0.5
        }).contextToRender([255, 0, 0, 255]);

        var fsFragDepth = '#extension GL_EXT_frag_depth : enable\n';

        fsFragDepth +=
            'void main()\n' +
            '{\n' +
            '    gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n';

        if (context.fragmentDepth) {
            fsFragDepth += '    gl_FragDepth';
            if (!context.webgl2) {
                fsFragDepth += 'EXT';
            }
            fsFragDepth += ' = 0.0;\n';
        }

        fsFragDepth += '}\n';

        var expected = [0, 255, 0, 255];
        expect({
            context : context,
            fragmentShader : fsFragDepth,
            depth : 1.0,
            clear : false
        }).contextToRender(expected);
    });

    it('get the draw buffers extension', function() {
        expect(context.drawBuffers).toBeDefined();
    });

    it('get the maximum number of draw buffers', function() {
        if (context.drawBuffers) {
            expect(ContextLimits.maximumDrawBuffers).toBeGreaterThanOrEqualTo(1);
        } else {
            expect(ContextLimits.maximumDrawBuffers).toEqual(1);
        }
    });

    it('get the maximum number of color attachments', function() {
        if (context.drawBuffers) {
            expect(ContextLimits.maximumColorAttachments).toBeGreaterThanOrEqualTo(4);
        } else {
            expect(ContextLimits.maximumColorAttachments).toEqual(1);
        }
    });

    it('can create a pick ID and retrieve an object by pick color', function() {
        var o = {};
        var pickId = context.createPickId(o);

        expect(pickId).toBeDefined();
        expect(context.getObjectByPickColor(pickId.color)).toBe(o);
    });

    it('throws when creating a pick ID without an object', function() {
        expect(function() {
            context.createPickId(undefined);
        }).toThrowDeveloperError();
    });

    it('returns undefined when retrieving an object by unknown pick color', function() {
        expect(context.getObjectByPickColor(Color.WHITE)).toBeUndefined();
    });

    it('throws when getObjectByPickColor is called without a color', function() {
        expect(function() {
            context.getObjectByPickColor(undefined);
        }).toThrowDeveloperError();
    });

    it('fails to construct (null canvas)', function() {
        expect(function() {
            return new Context();
        }).toThrowDeveloperError();
    });

    it('isDestroyed', function() {
        var c = createContext();
        expect(c.isDestroyed()).toEqual(false);
        c.destroyForSpecs();
        expect(c.isDestroyed()).toEqual(true);
    });

    it('destroying Context destroys objects in cache', function() {
        var c = createContext();
        var destroyableObject = jasmine.createSpyObj('destroyableObject', ['destroy']);
        c.cache.foo = destroyableObject;
        c.destroyForSpecs();
        expect(destroyableObject.destroy).toHaveBeenCalled();
    });

    it('non-destroyable objects are allowed in the cache', function() {
        var c = createContext();
        var nonDestroyableObject = {};
        c.cache.foo = nonDestroyableObject;
        c.destroyForSpecs();
    });

    it('returns the underling drawingBufferWidth', function() {
        var c = createContext(undefined, 1024, 768);
        expect(c.drawingBufferWidth).toBe(1024);
        c.destroyForSpecs();
    });

    it('returns the underling drawingBufferHeight', function() {
        var c = createContext(undefined, 1024, 768);
        expect(c.drawingBufferHeight).toBe(768);
        c.destroyForSpecs();
    });
}, 'WebGL');
